package cn.sciento.export;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import cn.sciento.core.oauth.CustomUserDetails;
import cn.sciento.core.oauth.DetailsHelper;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.ss.formula.functions.T;
import cn.sciento.core.helper.LanguageHelper;
import cn.sciento.core.redis.RedisHelper;
import cn.sciento.export.annotation.ExcelColumn;
import cn.sciento.export.annotation.ExcelExport;
import cn.sciento.export.annotation.ExcelSheet;
import cn.sciento.export.vo.ExportColumn;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;

public class ExportColumnHelper {
    private static final Logger logger = LoggerFactory.getLogger(ExportColumnHelper.class);

    private ThreadLocal<Long> idLocal = ThreadLocal.withInitial(() -> Long.valueOf(1L));

    private ThreadLocal<String> langLocal = ThreadLocal.withInitial(LanguageHelper::language);

    private ExportProperties properties;

    private RedisHelper redisHelper;

    public ExportColumnHelper(ExportProperties properties, RedisHelper redisHelper) {
        this.properties = properties;
        this.redisHelper = redisHelper;
    }

    public ExportColumn getExportColumn(ExcelExport excelExport) {
        Class<?> exportClass = excelExport.value();
        if (!exportClass.isAnnotationPresent((Class) ExcelSheet.class)) {
            logger.error("导出类[{}]请使用 @ExcelSheet 标注", exportClass.getName());
            return null;
        }
        ExportColumn root = getExportColumnFromClass(exportClass, excelExport.groups(), null);
        this.idLocal.set(Long.valueOf(1L));
        if (root != null) {
            root.setEnableAsync(Boolean.valueOf(false));
            if (this.properties != null)
                root.setEnableAsync(this.properties.getEnableAsync());
        }
        return root;
    }

    public ExportColumn getCheckedExportColumn(ExcelExport excelExport, Set<Long> checkedIds) {
        ExportColumn root = getExportColumn(excelExport);
        if (!root.isChecked())
            root.setChecked(checkedIds.contains(root.getId()));
        checkChildren(root, root.getChildren(), checkedIds);
        return root;
    }

    private void checkChildren(ExportColumn parent, List<ExportColumn> children, Set<Long> checkedIds) {
        if (CollectionUtils.isNotEmpty(children)) {
            boolean parentCheck = false;
            for (ExportColumn child : children) {
                if (checkedIds.contains(child.getId())) {
                    child.setChecked(true);
                    parentCheck = true;
                }
                if (child.getExcelColumn().child())
                    checkChildren(child, child.getChildren(), checkedIds);
            }
            parent.setChecked(parentCheck);
        }
    }

    public Set<String> getSelection(ExcelExport excelExport, Set<Long> checkedIds) {
        ExportColumn root = getCheckedExportColumn(excelExport, checkedIds);
        Set<String> selection = new HashSet<>(8);
        chooseChildren(root.getChildren(), selection);
        return selection;
    }

    private void chooseChildren(List<ExportColumn> children, Set<String> selection) {
        if (CollectionUtils.isNotEmpty(children))
            for (ExportColumn child : children) {
                if (child.hasChildren() && child.isChecked()) {
                    selection.add(child.getName());
                    chooseChildren(child.getChildren(), selection);
                }
            }
    }

    private ExportColumn getExportColumnFromClass(Class<?> exportClass, Class<?>[] groups, Long parentId) {
        ExcelSheet excelSheet = (ExcelSheet) AnnotationUtils.findAnnotation(exportClass, ExcelSheet.class);
        if (excelSheet == null)
            return null;
        Long rootId = autoIncrementId();
        ExportColumn root = new ExportColumn(rootId, getSheetName(exportClass, excelSheet), exportClass.getSimpleName(), parentId);
        root.setType("Class");
        String code = Optional.<Long>ofNullable(parentId).map(pid -> parentId + "" + rootId).orElse(rootId + "");
        root.setCode(code);
        root.setHasChildren(true);
        root.setExcelSheet(excelSheet);
        Field[] fields = exportClass.getDeclaredFields();
        List<ExportColumn> children = new ArrayList<>(fields.length);
        for (Field field : fields) {
            if (field.isAnnotationPresent((Class) ExcelColumn.class)) {
                ExcelColumn excelColumn = (ExcelColumn) AnnotationUtils.findAnnotation(field, ExcelColumn.class);
                if (excelColumn != null)
                    if (includeGroup(excelColumn.groups(), groups))
                        if (excelColumn.child()) {
                            Class<T> entityClass;
                            Type type = field.getGenericType();
                            if (type instanceof ParameterizedType) {
                                ParameterizedType parameterizedType = (ParameterizedType) type;
                                entityClass = (Class<T>) parameterizedType.getActualTypeArguments()[0];
                            } else {
                                entityClass = (Class<T>) type;
                            }
                            ExportColumn child = getExportColumnFromClass(entityClass, groups, rootId);
                            if (child != null) {
                                child.setOrder(excelColumn.order());
                                child.setName(field.getName());
                                child.setExcelColumn(excelColumn);
                                child.setChecked(excelColumn.defaultSelected());
                                children.add(child);
                            }
                        } else {
                            Long id = autoIncrementId();
                            ExportColumn column = new ExportColumn(id, getColumnTitle(excelSheet, excelColumn, field), field.getName(), rootId);
                            column.setOrder(excelColumn.order());
                            column.setType(field.getType().getSimpleName());
                            column.setCode(code + id);
                            column.setExcelColumn(excelColumn);
                            column.setChecked(excelColumn.defaultSelected());
                            children.add(column);
                        }
            }
        }
        children = (List<ExportColumn>) children.parallelStream().sorted(Comparator.comparing(ExportColumn::getOrder)).collect(Collectors.toList());
        root.setChildren(children);
        return root;
    }

    private Long autoIncrementId() {
        Long id = this.idLocal.get();
        this.idLocal.set(Long.valueOf(((Long) this.idLocal.get()).longValue() + 1L));
        return id;
    }

    private String getSheetName(Class<?> exportClass, ExcelSheet excelSheet) {
        String title = null;
        if (StringUtils.isNoneBlank(new CharSequence[]{excelSheet.promptKey(), excelSheet.promptCode()})) {
            CustomUserDetails userDetails = DetailsHelper.getUserDetails();
            if (userDetails != null && userDetails.getTenantId() != null)
                title = this.redisHelper.hshGet(excelSheet.promptKey() + "." + (String) this.langLocal.get() + "." + userDetails.getTenantId(), excelSheet.promptCode());
        }
        if (StringUtils.isBlank(title))
            if (Locale.SIMPLIFIED_CHINESE.toString().equalsIgnoreCase(this.langLocal.get())) {
                title = (String) StringUtils.defaultIfBlank(excelSheet.zh(), null);
            } else {
                title = (String) StringUtils.defaultIfBlank(excelSheet.en(), null);
            }
        if (StringUtils.isBlank(title))
            title = exportClass.getSimpleName();
        return title;
    }

    private String getColumnTitle(ExcelSheet excelSheet, ExcelColumn excelColumn, Field field) {
        String title = null;
        String promptKey = StringUtils.isNotBlank(excelColumn.promptKey()) ? excelColumn.promptKey() : excelSheet.promptKey();
        if (StringUtils.isNoneBlank(new CharSequence[]{promptKey, excelColumn.promptCode()}))
            title = this.redisHelper.hshGet(excelColumn.promptKey() + "." + (String) this.langLocal.get(), excelColumn.promptCode());
        if (StringUtils.isBlank(title))
            if (Locale.SIMPLIFIED_CHINESE.toString().equalsIgnoreCase(this.langLocal.get())) {
                title = (String) StringUtils.defaultIfBlank(excelColumn.zh(), null);
            } else {
                title = (String) StringUtils.defaultIfBlank(excelColumn.en(), null);
            }
        if (StringUtils.isBlank(title))
            title = StringUtils.capitalize(field.getName());
        return title;
    }

    private boolean includeGroup(Class<?>[] columnGroups, Class<?>[] groups) {
        if (ArrayUtils.isNotEmpty((Object[]) columnGroups) && ArrayUtils.isNotEmpty((Object[]) groups)) {
            for (Class<?> group : groups) {
                for (Class<?> columnGroup : columnGroups) {
                    if (columnGroup.equals(group))
                        return true;
                }
            }
        } else {
            return true;
        }
        return false;
    }

    public ExportColumnHelper() {
    }
}
